﻿using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using CoreAudioApi;
using System.Windows.Media.Animation;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Windows.Controls;

namespace VolumeMaster {
	/// <summary>
	/// Interaction logic for Window1.xaml
	/// </summary>
	public partial class OSDWindow : Window {

		#region Private Members
		private MMDevice curDefaultDevice;
		private Image MuteOffImage;
		private Image MuteOnImage;
		private Brush OriginalColor;
		private Storyboard FadeInOut;
		private ObjectAnimationUsingKeyFrames toggleVisible;
		private ObjectAnimationUsingKeyFrames toggleHidden;
		private float currentVolume;
		private bool isMuted;
		#endregion

		#region Public Properties
		private double _fid = 0D;
		private double _fode = 1.8D;
		public TimeSpan FadeInDuration {
			get { return TimeSpan.FromSeconds(_fid); }
			set { _fid = value.TotalSeconds; InitFadeInOut(); }
		}
		public TimeSpan FadeOutDelay {
			get { return TimeSpan.FromSeconds(_fode); }
			set { _fode = value.TotalSeconds; InitFadeInOut(); }
		}
		#endregion

		public OSDWindow() {
			Process currentProc = Process.GetCurrentProcess();
			
			//-- Kill any existing instances of VolumeMaster
			foreach (Process p in Process.GetProcessesByName(currentProc.ProcessName)) {
				if (p.Id != currentProc.Id) {
					p.Kill();
				}
			}
			InitializeComponent();

			//-- Set initial position the OSD
			InitInitialPosition();

			//-- Initilize the Volume Styles
			MuteOffImage = (Image)Resources["MuteOff"];
			MuteOnImage = (Image)Resources["MuteOn"];

			InitFadeInOut();

			//-- Grab Original Brush from ProgressBar
			OriginalColor = VolumeProgressBar.Foreground;

			RefreshDefaultAudioEndPoint();

			this.Show();
		}

		protected override void OnRender(DrawingContext drawingContext) {
			base.OnRender(drawingContext);
			UpdateVolume();
			
			HotKey.WheelMoved -= HotKey_WheelMoved;
			HotKey.MiddleDown -= HotKey_MiddleDown;
			HotKey.WheelMoved += HotKey_WheelMoved;
			HotKey.MiddleDown += HotKey_MiddleDown;
			HotKey.EnsureSubscribedToGlobalMouseEvents();

			AttachToWndProc();
		}

		#region Device Change Notifications
		private void AttachToWndProc() {
			HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
			source.AddHook(new HwndSourceHook(WndProc));
		}

		// system detected a new device 
		const int WM_DEVICECHANGE = 0x0219;
		// system detected a new device 
		//const int DBT_DEVICEARRIVAL = 0x8000;
		// new device has been removed
		//const int DBT_DEVICEREMOVECOMPLETE = 0x8001;


		private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
			
			switch (msg) {
				case WM_DEVICECHANGE:
					RefreshDefaultAudioEndPoint();
					break;
				default:
					break;
			}

			return IntPtr.Zero;
		}

		#endregion		

		private void RefreshDefaultAudioEndPoint() {
			MMDeviceEnumerator devEnum = new MMDeviceEnumerator();
			MMDevice oldDefaultDevice = curDefaultDevice;
			curDefaultDevice = devEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);

			//-- Attach to the volume change.
			curDefaultDevice.AudioEndpointVolume.OnVolumeNotification +=
				AudioEndpointVolume_OnVolumeNotification;

			//-- If we had an old device, then we need to detach from that
			//OnVolumeNotification because if it's re-inabled, we'll have
			//duplicate events
			if (oldDefaultDevice != null) {
				oldDefaultDevice.AudioEndpointVolume.OnVolumeNotification -=
					AudioEndpointVolume_OnVolumeNotification;
				//-- If the oldDefault device was a different device, we should
				//display the volume of the current device.
				if (oldDefaultDevice.ID != curDefaultDevice.ID)
					UpdateVolume();
			}
		}

		private void InitInitialPosition() {

			Taskbar tb = new Taskbar();
			Rect WorkArea = SystemParameters.WorkArea;

			switch (tb.Position) {
				case TaskbarPosition.Left:
					Top = WorkArea.Height - Height - 10;
					Left = WorkArea.Left + 10;
					break;
				case TaskbarPosition.Top:
					Top = WorkArea.Top + 10;
					Left = WorkArea.Width - Width - 60;
					break;
				case TaskbarPosition.Right:
					Top = WorkArea.Height - Height - 10;
					Left = WorkArea.Width - Width - 10;
					break;
				case TaskbarPosition.Bottom:
					Top = WorkArea.Height - 10 - Height;
					Left = WorkArea.Width - Width - 60;
					break;
				case TaskbarPosition.Unknown:
				default:
					Top = (WorkArea.Height - Height) / 2;
					Left = (WorkArea.Width - Width) / 2;
					break;

			}
		}

		private void InitFadeInOut() {
			FadeInOut = new Storyboard();

			toggleVisible = new ObjectAnimationUsingKeyFrames();
			Storyboard.SetTarget(toggleVisible, this);
			Storyboard.SetTargetProperty(toggleVisible, new PropertyPath(Window.VisibilityProperty));
			toggleVisible.KeyFrames.Add(new DiscreteObjectKeyFrame(Visibility.Visible, TimeSpan.Zero));
		
			toggleHidden = new ObjectAnimationUsingKeyFrames();
			toggleHidden.BeginTime = FadeOutDelay;
			Storyboard.SetTarget(toggleHidden, this);
			Storyboard.SetTargetProperty(toggleHidden, new PropertyPath(Window.VisibilityProperty));
			toggleHidden.KeyFrames.Add(new DiscreteObjectKeyFrame(Visibility.Hidden, TimeSpan.Zero));

			//-- Add the toggleVisible & toggleHidden animations
			FadeInOut.Children.Add(toggleVisible);
			FadeInOut.Children.Add(toggleHidden);
		}

		private void UpdateVolume() {
			UpdateVolume(curDefaultDevice.AudioEndpointVolume.MasterVolumeLevelScalar, curDefaultDevice.AudioEndpointVolume.Mute);
		}

		private void UpdateVolume(float volume, bool muted) {
			if (volume != currentVolume || muted != isMuted) {

				Dispatcher.BeginInvoke(new Action(delegate() {

					currentVolume = volume;
					isMuted = muted;

					if (muted) {
						MuteButton.Content = MuteOnImage;
						VolumeProgressBar.IsEnabled = false;
						VolumeProgressBar.Foreground = Brushes.Red;
					} else {
						MuteButton.Content = MuteOffImage;
						VolumeProgressBar.Foreground = OriginalColor;
					}

					VolumeProgressBar.Value = volume;

					FadeInOut.Begin();
				}));
			}
		}

		private void ToggleMute() {
			try {
				curDefaultDevice.AudioEndpointVolume.Mute = !curDefaultDevice.AudioEndpointVolume.Mute;
			} catch {
				//-- If the default AudioEnd point no longer exists, we need to
				// get a handle to the new default Audio Endpoint
				RefreshDefaultAudioEndPoint();
				try {
					curDefaultDevice.AudioEndpointVolume.Mute = !curDefaultDevice.AudioEndpointVolume.Mute;
				} catch { }
			}
		}

		#region Event Handlers

		private void HotKey_MiddleDown(object sender, HotKey.MouseEventArgs e) {
			ToggleMute();
		}

		private void HotKey_WheelMoved(object sender, HotKey.MouseEventArgs e) {
			try {
				if (e.Delta > 0)
					curDefaultDevice.AudioEndpointVolume.VolumeStepUp();
				else
					curDefaultDevice.AudioEndpointVolume.VolumeStepDown();
			} catch {
				//-- If the default AudioEnd point no longer exists, we need to
				// get a handle to the new default Audio Endpoint
				RefreshDefaultAudioEndPoint();
				try {
					if (e.Delta > 0)
						curDefaultDevice.AudioEndpointVolume.VolumeStepUp();
					else
						curDefaultDevice.AudioEndpointVolume.VolumeStepDown();
				} catch { }
			}
		}

		/// <summary>
		/// This fires any time the volume changes or is muted.
		/// </summary>
		private void AudioEndpointVolume_OnVolumeNotification(AudioVolumeNotificationData data) {
			UpdateVolume(data.MasterVolume, data.Muted);
		}

		#region UI Events
		private void Exit_Click(object sender, RoutedEventArgs e) {
			MessageBoxResult r =
				MessageBox.Show("Are you sure you want to exit?", string.Empty, MessageBoxButton.YesNo, MessageBoxImage.Question);
			if (r == MessageBoxResult.Yes)
				App.Current.Shutdown();
		}

		private void mainWin_Deactivated(object sender, EventArgs e) {
			try {
				FadeInOut.Stop();
				this.Hide();
			} catch { }
		}

		private void mainWin_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) {
			this.DragMove();
		}

		private void mainWin_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e) {
			FadeInOut.Children.Remove(toggleHidden);
			FadeInOut.Begin();
		}

		private void mainWin_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e) {
			FadeInOut.Children.Add(toggleHidden);
			FadeInOut.Begin();
		}

		private void MixerLink_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) {
			Process.Start("sndvol");
			e.Handled = true;
		}

		private void MuteButton_Click(object sender, RoutedEventArgs e) {
			ToggleMute();
		}

		private void Speaker_Click(object sender, RoutedEventArgs e) {
			ProcessStartInfo psi = new ProcessStartInfo("mmsys.cpl");
			psi.Arguments = "sounds";
			Process.Start(psi);
		}
		#endregion 
		#endregion
	}
}
